package com.bdbit.ChargingStation.services;

import com.bdbit.ChargingStation.wx.AesUtil;
import com.bdbit.ChargingStation.wx.WxAPI;
import com.bdbit.ChargingStation.wx.WxConfig;
import com.bdbit.ChargingStation.wx.results.AccessTokenCode;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StreamUtils;

import javax.annotation.PostConstruct;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;

@Slf4j
@Service
public class WxService {

    private final Gson gson = new GsonBuilder().serializeNulls().create();

    /**
     * 通过code换取网页授权access_token
     * 返回对应的openid
     */
    public String getAccessToken(String code) throws IOException {

        StringBuilder urlBuilder = new StringBuilder(WxAPI.WEBSITE_ACCESS_TOKEN);
        urlBuilder.append("?appid=").append(WxConfig.appid);
        urlBuilder.append("&secret=").append(WxConfig.appSecret);
        urlBuilder.append("&code=").append(code);
        urlBuilder.append("&grant_type=").append("authorization_code");

        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {

            // 将Url与params进行组装
            HttpGet httpGet = new HttpGet(urlBuilder.toString());
            // TODO ->httpclient.execute(....)的返回值
            String content = httpClient.execute(httpGet, response -> {
                //  解析请求响应
                final HttpEntity entity = response.getEntity();
                int status = response.getCode();
                if (status == 200) {
                    return EntityUtils.toString(entity);
                }
                // ensure response is fully consumed
                EntityUtils.consume(entity);
                return null;
            });
            if (content != null) {
                // 将JSON数据映射为对应Java对象
                System.out.println(content);
                return gson.fromJson(content, AccessTokenCode.class).getOpenid();
            }
        }
        return null;
    }

    // 在 WxService.java 中添加
    @PostConstruct
    public void initCertificates() {
        try {
            log.info("开始初始化微信支付证书...");
            log.info("使用商户号: {}", WxConfig.merchantId);
            log.info("使用APIv3密钥: {}...",
                    (WxConfig.apiV3key != null && WxConfig.apiV3key.length() > 10)
                            ? WxConfig.apiV3key.substring(0, 10) + "..."
                            : "null");

            // 记录私钥长度
            if (WxConfig.apiV3key != null) {
                log.info("APIv3密钥长度: {}", WxConfig.apiV3key.length());
            }

            // 首先验证私钥格式 - 新增的关键步骤
            validatePrivateKey();

            // 刷新证书
            refreshWechatCertificates();
            log.info("微信支付证书初始化成功，加载证书数量: {}", CERT_CACHE.size());
        } catch (Exception e) {
            log.error("微信支付证书初始化失败", e);
            log.error("请检查以下配置是否正确:");
            log.error("1. WxConfig.merchantId: {}", WxConfig.merchantId);
            log.error("2. WxConfig.apiV3key: {}...",
                    (WxConfig.apiV3key != null && WxConfig.apiV3key.length() > 10)
                            ? WxConfig.apiV3key.substring(0, 10) + "..."
                            : "null");
            log.error("3. WxConfig.certSerialNo: {}", WxConfig.certSerialNo);
        }
    }

    // 在 WxService.java 中添加
    public PublicKey getPublicKeyBySerial(String serialNo) {
        return CERT_CACHE.get(serialNo);
    }

    /**
     * 微信支付回调签名验证（核心安全方法）
     *
     * @param bodyStream 原始请求体流
     * @param signature  微信回调头部的签名(Wechatpay-Signature)
     * @param timestamp  微信回调头部的时间戳(Wechatpay-Timestamp)
     * @param nonce      微信回调头部的随机数(Wechatpay-Nonce)
     * @param serialNo   微信回调头部的证书序列号(Wechatpay-Serial)
     * @return 验证是否通过
     */
    public boolean verifySignature(InputStream bodyStream,
                                   String signature,
                                   String timestamp,
                                   String nonce,
                                   String serialNo) {
        try {
            // 1. 构建验签名串
            String body = StreamUtils.copyToString(bodyStream, StandardCharsets.UTF_8);
            String message = buildSignMessage(timestamp, nonce, body);

            // 2. 获取平台公钥（根据证书序列号）
            PublicKey publicKey = getWechatPublicKey(serialNo);
            if (publicKey == null) {
                // 如果没有缓存，实时下载证书
                refreshWechatCertificates();
                publicKey = getWechatPublicKey(serialNo);
                if (publicKey == null) {
                    return false;
                }
            }

            // 3. 使用公钥验证签名
            return verifyWithPublicKey(
                    publicKey,
                    message.getBytes(StandardCharsets.UTF_8),
                    Base64.getDecoder().decode(signature)
            );
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 构建待验签的原始字符串
     * 格式：timestamp + "\n" + nonce + "\n" + body + "\n"
     */
    private String buildSignMessage(String timestamp, String nonce, String body) {
        return timestamp + "\n" + nonce + "\n" + body + "\n";
    }

    /**
     * 使用公钥验证签名
     *
     * @param publicKey 公钥
     * @param message   原始信息
     * @param signature 签名值（Base64解码后）
     */
    private boolean verifyWithPublicKey(PublicKey publicKey, byte[] message, byte[] signature)
            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {

        Signature signer = Signature.getInstance("SHA256withRSA");
        signer.initVerify(publicKey);
        signer.update(message);
        return signer.verify(signature);
    }

    // 证书缓存（序列号 -> 公钥）
    private static final Map<String, PublicKey> CERT_CACHE = new HashMap<>();

    /**
     * 获取缓存的平台证书公钥
     */
    public PublicKey getWechatPublicKey(String serialNo) {
        return CERT_CACHE.get(serialNo);
    }

    /**
     * 刷新微信平台证书（定时任务调用）
     */
    public void refreshWechatCertificates() throws Exception {
        try(CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpGet httpGet = new HttpGet(WxAPI.CERTIFICATES);
            httpGet.addHeader("Accept", "application/json");
            httpGet.addHeader("Authorization", getTokenAuthorization());
            log.info("请求微信证书接口: {}", WxAPI.CERTIFICATES);

            httpClient.execute(httpGet, response -> {
                int status = response.getCode();
                if (status != 200) {
                    // 读取错误信息
                    String errorBody = EntityUtils.toString(response.getEntity());
                    log.error("获取证书失败，状态码: {}，错误信息: {}", status, errorBody);
                    return null;
                }

                HttpEntity entity = response.getEntity();
                if (entity == null) {
                    log.error("获取证书失败，响应体为空");
                    return null;
                }

                String json = EntityUtils.toString(entity);
                log.debug("证书接口返回: {}", json);

                // 解析JSON
                WechatCertResponse certResponse = gson.fromJson(json, WechatCertResponse.class);
                if (certResponse == null || certResponse.getData() == null) {
                    log.error("证书解析失败，响应格式错误");
                    return null;
                }

                log.info("成功获取证书，数量: {}", certResponse.getData().size());

                // 清除旧缓存
                CERT_CACHE.clear();

                // 解析并存储证书
                for (WechatCertResponse.CertData data : certResponse.getData()) {
                    WechatCertResponse.EncryptCertificate encryptCert = data.getEncryptCertificate();
                    if (encryptCert == null) {
                        log.error("加密证书数据为空: serial={}", data.getSerialNo());
                        continue;
                    }

                    try {
                        // 记录加密证书信息
                        log.info("加密证书信息:");
                        log.info("Algorithm: {}", encryptCert.getAlgorithm());
                        log.info("Associated Data: {}", encryptCert.getAssociatedData());
                        log.info("Nonce: {}", encryptCert.getNonce());
                        log.info("Ciphertext 长度: {}", encryptCert.getCiphertext().length());
                        log.info("Ciphertext 前50字符: {}",
                                encryptCert.getCiphertext().substring(0, Math.min(50, encryptCert.getCiphertext().length())));

                        // 解密证书
                        String publicKeyStr = decryptCertificate(
                                encryptCert.getCiphertext(),
                                encryptCert.getAssociatedData(),
                                encryptCert.getNonce()
                        );

                        if (publicKeyStr == null || publicKeyStr.isEmpty()) {
                            log.error("证书解密失败: serial={}", data.getSerialNo());
                            continue;
                        }

                        // 记录解密后的证书
                        log.info("解密后的证书长度: {}", publicKeyStr.length());
                        log.info("解密后的证书前100字符: {}",
                                publicKeyStr.substring(0, Math.min(100, publicKeyStr.length())));

                        // 解析证书
                        PublicKey publicKey = parseCertificate(publicKeyStr.getBytes(StandardCharsets.UTF_8));
                        CERT_CACHE.put(data.getSerialNo(), publicKey);
                        log.info("成功加载证书: serial={}", data.getSerialNo());
                    } catch (Exception e) {
                        log.error("证书解析失败: serial={}", data.getSerialNo(), e);
                    }
                }
                return null;
            });
        }
    }
    /**
     * 解密证书
     */
    private String decryptCertificate(String ciphertext, String associatedData, String nonce) {
        try {
            log.info("开始解密证书...");
            log.info("APIv3密钥长度: {}", WxConfig.apiV3key.length());
            log.info("关联数据: {}", associatedData);
            log.info("Nonce: {}", nonce);
            log.info("密文长度: {}", ciphertext.length());

            AesUtil aesUtil = new AesUtil(WxConfig.apiV3key.getBytes(StandardCharsets.UTF_8));
            String result = aesUtil.decryptToString(
                    associatedData.getBytes(StandardCharsets.UTF_8),
                    nonce.getBytes(StandardCharsets.UTF_8),
                    ciphertext
            );

            if (result == null) {
                log.error("解密返回null");
            } else {
                log.info("解密成功，结果长度: {}", result.length());
            }

            return result;
        } catch (Exception e) {
            log.error("证书解密失败", e);
            return null;
        }
    }
    public void validatePrivateKey() {
        try {
            String testData = "test";
            byte[] sign = signWithPrivateKey(testData.getBytes());
            log.info("私钥验证成功，签名长度: {}", sign.length);
        } catch (Exception e) {
            log.error("私钥验证失败", e);
            log.error("可能原因:");
            log.error("1. 私钥格式不正确（需要PKCS#8格式）");
            log.error("2. 私钥未正确Base64编码");
            log.error("3. 私钥包含多余字符（如BEGIN/END标签）");

            // 打印私钥前20个字符用于调试
            if (WxConfig.apiV3key != null && WxConfig.apiV3key.length() > 20) {
                log.error("原始私钥前20字符: {}", WxConfig.apiV3key.substring(0, 20));
            }
        }
    }

    /**
     * 生成授权令牌 (格式规范: https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml)
     */
    private String getTokenAuthorization() throws Exception {
        try {
            String method = "GET";
            String url = "/v3/certificates";
            String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
            String nonce = generateNonce();
            String body = "";

            // 构建签名字符串（确保最后有换行符）
            String signStr = method + "\n" + url + "\n" + timestamp + "\n" + nonce + "\n" + body + "\n";
            log.info("待签名字符串: \n{}", signStr);  // 记录待签名字符串

            // 使用商户私钥签名
            byte[] signBytes = signWithPrivateKey(signStr.getBytes(StandardCharsets.UTF_8));
            String signature = Base64.getEncoder().encodeToString(signBytes);
            log.info("生成签名: {}", signature);  // 记录签名

            // 构建Authorization头（关键修复）
            String authHeader = "WECHATPAY2-SHA256-RSA2048 " +
                    "mchid=\"" + WxConfig.merchantId + "\"," +
                    "nonce_str=\"" + nonce + "\"," +  // 使用 nonce_str
                    "timestamp=\"" + timestamp + "\"," +  // 使用 timestamp
                    "serial_no=\"" + WxConfig.certSerialNo + "\"," +
                    "signature=\"" + signature + "\"";

            log.info("生成Authorization头: {}", authHeader);  // 记录完整头
            return authHeader;
        } catch (Exception e) {
            log.error("生成授权令牌失败", e);
            throw e;
        }
    }

    /**
     * 解析证书内容获取公钥
     */
    private PublicKey parseCertificate(byte[] certificateBytes) throws Exception {
        try {
            String pem = new String(certificateBytes, StandardCharsets.UTF_8);
            log.info("待解析证书长度: {}", pem.length());

            // 检查是否是PEM格式
            if (pem.contains("-----BEGIN CERTIFICATE-----")) {
                log.info("检测到PEM格式证书");

                // 提取Base64部分
                String base64 = pem.replace("-----BEGIN CERTIFICATE-----", "")
                        .replace("-----END CERTIFICATE-----", "")
                        .replaceAll("\\s", ""); // 移除所有空白字符

                log.info("提取Base64证书内容，长度: {}", base64.length());
                certificateBytes = Base64.getDecoder().decode(base64);
            } else {
                log.info("未检测到PEM格式，直接解析");
            }

            CertificateFactory factory = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate) factory.generateCertificate(
                    new ByteArrayInputStream(certificateBytes)
            );

            // 验证证书信息
            log.info("证书主题: {}", cert.getSubjectDN());
            log.info("证书颁发者: {}", cert.getIssuerDN());
            log.info("证书有效期: {} 至 {}", cert.getNotBefore(), cert.getNotAfter());

            return cert.getPublicKey();
        } catch (Exception e) {
            log.error("证书解析失败，输入数据长度: {}", certificateBytes.length);
            if (certificateBytes.length > 0) {
                log.error("输入数据前100字节: {}",
                        new String(certificateBytes, 0, Math.min(100, certificateBytes.length)));
            }
            throw e;
        }
    }

    /**
     * 使用商户私钥签名
     */
    private byte[] signWithPrivateKey(byte[] data) throws Exception {
        try {
            // 1. 从WxConfig获取私钥内容（确保是文件内容）
            String privateKeyContent = WxConfig.privateKey;

            // 2. 清理私钥格式
            String cleanedKey = privateKeyContent
                    .replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s", ""); // 移除所有空白字符

            // 3. 记录清理后的私钥前20个字符用于调试
            log.info("清理后的私钥前20字符: {}", cleanedKey.substring(0, Math.min(20, cleanedKey.length())));

            // 4. 尝试Base64解码
            byte[] keyBytes = Base64.getDecoder().decode(cleanedKey);
            log.info("Base64解码成功，密钥长度: {}", keyBytes.length);

            // 5. 验证密钥长度（确保是有效的私钥）
            if (keyBytes.length < 100) {
                throw new IllegalArgumentException("私钥长度过短，可能配置错误");
            }

            // 6. 尝试加载PKCS#8格式
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
            log.info("成功加载PKCS#8格式私钥");

            // 7. 创建签名
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(privateKey);
            signature.update(data);
            return signature.sign();
        } catch (Exception e) {
            log.error("私钥签名失败，请检查私钥格式", e);
            throw e;
        }
    }

    /**
     * 生成随机字符串
     */
    private String generateNonce() {
        byte[] nonceBytes = new byte[16];
        new SecureRandom().nextBytes(nonceBytes);
        return Base64.getEncoder().encodeToString(nonceBytes);
    }

    // 微信证书响应结构
    // 将静态嵌套类移出WxService类

}
